package com.lambkit.core;

import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lambkit.core.cache.ICacheFactory;
import com.lambkit.core.config.ConfigCenterContainer;
import com.lambkit.core.config.SettingConfigCenter;
import com.lambkit.core.pipeline.DefaultPipeLineFactory;
import com.lambkit.core.pipeline.IPipeLineFactory;
import com.lambkit.core.pipeline.PipeLineService;
import com.lambkit.core.pipeline.Valve;
import com.lambkit.core.service.*;
import com.lambkit.core.service.impl.DefaultBeanFactory;
import com.lambkit.core.service.impl.DefaultClassOperateFactory;
import com.lambkit.core.service.impl.DefaultResourceFactory;
import com.lambkit.util.JsonKit;
import com.lambkit.util.Printer;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Lambkit的上下文
 * @author yangyong(孤竹行)
 */
public class LambkitContext extends BaseContext {

    public static final String MAIN_APP = "main";
    /**
     * 启动类，用于启动时，获取启动类上的注解
     */
    private Class<?> targetClass;

    private LambkitConfig lambkitConfig;

    private Map<String, LambkitApp> apps = new ConcurrentHashMap<>();

    private BeanContainer beanContainer = new BeanContainer();

    private IPipeLineFactory pipeLineFactory = new DefaultPipeLineFactory();

    private BeanFactory beanFactory = new DefaultBeanFactory();

    private ResourceFactory resourceFactory = new DefaultResourceFactory();

    private ClassOperateFactory classOperateFactory = new DefaultClassOperateFactory();

    private ICacheFactory cacheFactory;

    private ConfigCenterContainer configCenterContainer = new ConfigCenterContainer();

    // 是否开发模式
    private boolean devMode = false;

    /////////////////////////////////////////////////////////////
    // 系统运行的管道
    AppRunningPipLine initPipeLine = new AppRunningPipLine();
    AppRunningPipLine startPipeLine = AppRunningPipLine.by(new AppStartValve());
    AppRunningPipLine stopPipeLine = AppRunningPipLine.by(new AppStopValve());

   /////////////////////////////////////////////////////////////
    public void init() {
        if(configCenterContainer.size()==0) {
            configCenterContainer.addConfigCenter(new SettingConfigCenter("lambkit"));
        }
        lambkitConfig = Lambkit.config(LambkitConfig.class);
        devMode = lambkitConfig.isDevMode();
        // 初始化管道
        ClassPathResource resource = new ClassPathResource("config/context.json");
        if(resource!=null) {
            String startPipLineConfig = resource.readUtf8Str();
            if(StrUtil.isNotBlank(startPipLineConfig)) {
                JSONObject spljson = JSON.parseObject(startPipLineConfig);

                String serverLifycycleConfig = JsonKit.getString(spljson, "lambkit.server.lifecycle");
                if(StrUtil.isNotBlank(serverLifycycleConfig)) {
                    Class serverLifycycleClass = ClassUtil.loadClass(serverLifycycleConfig);
                    if(serverLifycycleClass!=null) {
                        setAttr("serverLifecycleClass", serverLifycycleClass);
                    }
                }

                JSONArray initPipeLines = JsonKit.getJSONArray(spljson, "lambkit.pipeline.init");
                for(int i=0; i<initPipeLines.size(); i++) {
                    JSONObject initValve = initPipeLines.getJSONObject(i);
                    String valveClass = initValve.getString("valve");
                    Valve valve = ReflectUtil.newInstance(valveClass);
                    initPipeLine.addValve(valve);
                }
                if(initPipeLine.firstValve()==null) {
                    initPipeLine.addValve(new AppScanValve())
                            .addValve(new AppCreateValve())
                            .addValve(new AppInitValve());
                }

                JSONArray startPipeLines = JsonKit.getJSONArray(spljson, "lambkit.pipeline.start");
                for(int i=0; i<startPipeLines.size(); i++) {
                    JSONObject startValve = startPipeLines.getJSONObject(i);
                    String valveClass = startValve.getString("valve");
                    Valve valve = ReflectUtil.newInstance(valveClass);
                    startPipeLine.addValve(valve);
                }

                JSONArray stopPipeLines = JsonKit.getJSONArray(spljson, "lambkit.pipeline.stop");
                for(int i=0; i<stopPipeLines.size(); i++) {
                    JSONObject stopValve = stopPipeLines.getJSONObject(i);
                    String valveClass = stopValve.getString("valve");
                    Valve valve = ReflectUtil.newInstance(valveClass);
                    stopPipeLine.addValve(valve);
                }
            }
        }
    }

    public LambkitApp app() {
        return apps.get(MAIN_APP);
    }

    public LambkitApp app(String name)  {
        return apps.get(name);
    }

    public LambkitApp createApp(String name, Class<? extends LambkitApp> appClass)  {
        LambkitApp app = ReflectUtil.newInstance(appClass, name);
        Printer.print(this, "starter", "Create App Name {}, class {}", name, appClass.getName());
        apps.put(name, app);
        return app;
    }
    public void removeApp(String name)  {
        apps.remove(name);
    }

    public Collection<LambkitApp> appCollection()  {
        return apps.values();
    }

    public int appSize()  {
        return apps.size();
    }
    ///////////////////////////////////////////////////////////////
    public <T> T getBean(Class<T> clazz) {
        T obj = beanContainer.getBean(clazz);
        if(obj == null && getBeanFactory() != null) {
            obj = getBeanFactory().get(clazz);
        }
        return obj;
    }

    public void setBean(Class clazz, Object bean) {
        //Printer.print(this, "starter", "LambkitContext setBean {} - {}.", clazz.getName(), bean.getClass().getName());
        beanContainer.addBean(clazz, bean);
    }

    public String getResourcePathType() {
        return lambkitConfig.getResourcePathType();
    }

    public String getResourcePath() {
        return lambkitConfig.getResourcePath();
    }

    public String getTemplatePath() {
        return lambkitConfig.getTemplatePath();
    }
    ///////////////////////////////////////////////////////////////

    public String getConfigContext(String key) {
        ClassPathResource resource = new ClassPathResource("config/context.json");
        if(resource!=null) {
            String startPipLineConfig = resource.readUtf8Str();
            if (StrUtil.isNotBlank(startPipLineConfig)) {
                JSONObject spljson = JSON.parseObject(startPipLineConfig);
                return JsonKit.getString(spljson, key);
            }
        }
        return null;
    }

    ///////////////////////////////////////////////////////////////
    public LambkitConfig getLambkitConfig() {
        return lambkitConfig;
    }

    public void setLambkitConfig(LambkitConfig lambkitConfig) {
        this.lambkitConfig = lambkitConfig;
    }

    public ClassOperateService classOperateService() {
        return getClassOperateFactory().getClassOperateService();
    }

    public ResourceService resourceService() {
        return getResourceFactory().getResourceService();
    }

    public PipeLineService pipeLineService() {
        return getPipeLineFactory().getPipeLineService();
    }

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        Printer.print(this, "starter", "beanFactory class: " + beanFactory.getClass().getName());
        this.beanFactory = beanFactory;
    }

    public ClassOperateFactory getClassOperateFactory() {
        return classOperateFactory;
    }

    public void setClassOperateFactory(ClassOperateFactory classOperateFactory) {
        this.classOperateFactory = classOperateFactory;
    }

    public IPipeLineFactory getPipeLineFactory() {
        return pipeLineFactory;
    }

    public void setPipeLineFactory(IPipeLineFactory pipeLineFactory) {
        this.pipeLineFactory = pipeLineFactory;
    }

    public ResourceFactory getResourceFactory() {
        return resourceFactory;
    }

    public void setResourceFactory(ResourceFactory resourceFactory) {
        this.resourceFactory = resourceFactory;
    }

    public ICacheFactory getCacheFactory() {
        return cacheFactory;
    }

    public void setCacheFactory(ICacheFactory cacheFactory) {
        this.cacheFactory = cacheFactory;
    }

    public ConfigCenterContainer getConfigCenterContainer() {
        return configCenterContainer;
    }

    public void setConfigCenterContainer(ConfigCenterContainer configCenterContainer) {
        this.configCenterContainer = configCenterContainer;
    }

    public boolean isDevMode() {
        return devMode;
    }

    public boolean getDevMode() {
        return devMode;
    }

    public void setDevMode(boolean devMode) {
        this.devMode = devMode;
    }

    public AppRunningPipLine getInitPipeLine() {
        return initPipeLine;
    }

    public void setInitPipeLine(AppRunningPipLine initPipeLine) {
        this.initPipeLine = initPipeLine;
    }

    public AppRunningPipLine getStartPipeLine() {
        return startPipeLine;
    }

    public void setStartPipeLine(AppRunningPipLine startPipeLine) {
        this.startPipeLine = startPipeLine;
    }

    public AppRunningPipLine getStopPipeLine() {
        return stopPipeLine;
    }

    public void setStopPipeLine(AppRunningPipLine stopPipeLine) {
        this.stopPipeLine = stopPipeLine;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
    }
}
